﻿using MathNet.Numerics.Statistics;
using System;
using System.Collections.Generic;
using System.Linq;

namespace LpbPrj.Client
{
    /// <summary>
    /// 数学计算公式
    /// 特别注意：四舍五入的地方
    /// 必须使用第七个重载方法：Math.Round(double, Int32, MidpointRounding)  （举例：Math.Round(526.925, 2,MidpointRounding.AwayFromZero)）
    /// 不能直接使用double进行计算
    /// </summary>
    public static class NumbersExtend
    {
        public static bool String2Double(this string str, out double? retDouble, int digits = 2)
        {
            retDouble = null;
            bool ret = false;

            if (string.IsNullOrEmpty(str)) return true;

            double data = double.NaN;
            if (double.TryParse(str, out data))
            {
                retDouble = (double)Math.Round(data, digits, MidpointRounding.AwayFromZero);
                ret = true;
            }

            return ret;
        }

        public static bool String2Int(this string str, out int? retInt)
        {
            retInt = null;
            bool ret = false;

            if (string.IsNullOrEmpty(str)) return true;

            int data = -1;
            if (int.TryParse(str, out data))
            {
                retInt = data;
                ret = true;
            }

            return ret;
        }

        public static string ToStringEx(this object value)
        {
            if (value != null)
            {
                return value.ToString().Replace(" ", "");
            }
            return null;
        }

        public static bool IsNaN(this double value) => double.IsNaN(value);

        public static int? ToInt(this int value)
        {
            if (value <= 0) return null;
            return value;
        }

        public static double? ToDouble(this float value, int? digits = null)
        {
            if (float.IsNaN(value)) return null;
            //if (value <= 0.0) return null;

            double roundDouble = value;
            if (digits.HasValue)
            {
                roundDouble = (double)Math.Round(value, digits.Value, MidpointRounding.AwayFromZero);
            }
            return roundDouble;
        }

        public static double? ToDouble(this double value, int? digits = null)
        {
            if (double.IsNaN(value)) return null;

            double roundDouble = value;
            if (digits.HasValue)
            {
                roundDouble = (double)Math.Round(value, digits.Value, MidpointRounding.AwayFromZero);
            }
            return roundDouble;
        }

        public static double? ToDouble(this double? value, int? digits = null)
        {
            if (!value.HasValue) return null;
            if (double.IsNaN(value.Value)) return null;

            double roundDouble = value.Value;
            if (digits.HasValue)
            {
                roundDouble = (double)Math.Round(roundDouble, digits.Value, MidpointRounding.AwayFromZero);
            }
            return roundDouble;
        }

        public static decimal? ToDecimal(this double value, int? digits = null)
        {
            if (double.IsNaN(value)) return null;

            double roundDouble = value;
            if (digits.HasValue)
            {
                roundDouble = (double)Math.Round(value, digits.Value, MidpointRounding.AwayFromZero);
            }
            return Convert.ToDecimal(roundDouble);
        }

        public static int? ToInt(this double value)
        {
            if (double.IsNaN(value)) return null;
            else return (int)Math.Round(value, 0, MidpointRounding.AwayFromZero);
        }

        public static byte? ToByte(this double value)
        {
            if (double.IsNaN(value)) return null;
            else return (byte)Math.Round(value, 0, MidpointRounding.AwayFromZero);
        }

        public static double? ToKR(this double? param)
        {
            if (!param.HasValue) return null;
            if (double.IsNaN(param.Value)) return null;

            if (!param.HasValue || ((param.Value - 0.0001) < 0)) return null;
            //double ret = 33.75 * 10 / (double)param;
            //return System.Convert.ToDecimal(Math.Round(ret, 2, MidpointRounding.AwayFromZero));
            return 33.75 * 10 / param.Value;
        }

        public static string ToShowString(this double? value, int digits = 2, string substitutes = null, string prefix = null, string suffix = null)
        {
            string output = null;
            try
            {
                if (value.HasValue && !double.IsNaN(value.Value))
                {
                    double roundDouble = (double)Math.Round(value.Value, digits, MidpointRounding.AwayFromZero);

                    output = digits == 0 ? ((int)roundDouble).ToString() : roundDouble.ToString(string.Format("f{0}", digits));

                    if (!string.IsNullOrEmpty(prefix))
                    {
                        output = prefix + output;
                    }

                    if (!string.IsNullOrEmpty(suffix))
                    {
                        output = output + suffix;
                    }
                }
                else
                {
                    output = substitutes;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                output = null;
            }

            return output;
        }

        public static string ToFormatPowerString(this double? value, int digits = 2, string substitutes = null, string prefix = null, string suffix = null, bool awayFromZero = true)
        {
            string output = null;
            if (value.HasValue && !double.IsNaN(value.Value))
            {
                double roundDouble = (double)Math.Round(value.Value, digits, MidpointRounding.AwayFromZero);
                if (awayFromZero == false)
                {
                    roundDouble = (double)Math.Round(value.Value, digits, MidpointRounding.ToZero);
                }

                if (roundDouble < 0.0)
                {
                    //output = string.Format("{0:0.00}", roundDouble);
                    output = roundDouble.ToString(string.Format("f{0}", digits));
                }
                else
                {
                    //output = string.Format("{0:+0.00}", roundDouble);
                    output = roundDouble.ToString(string.Format("f{0}", digits));
                    output = "+" + output;
                }


                if (!string.IsNullOrEmpty(prefix))
                {
                    output = prefix + output;
                }

                if (!string.IsNullOrEmpty(suffix))
                {
                    output = output + suffix;
                }
            }
            else
            {
                output = substitutes;
            }

            return output;
        }

        public static string ToPercentString(this double? value, double dataTotal = 1.0, int digits = 0, string suffix = "%")
        {
            string output = null;
            if (value.HasValue && !double.IsNaN(value.Value))
            {
                double dataConvert = (double)Math.Round((value.Value / dataTotal * 100), digits, MidpointRounding.AwayFromZero);
                output = (digits == 0 ? ((int)dataConvert).ToString() : dataConvert.ToString(string.Format("f{0}", digits)));

                if (!string.IsNullOrEmpty(suffix))
                {
                    output = output + suffix;
                }
            }

            return output;
        }

        public static string ToShowString(this decimal? value, int digits = 2, string substitutes = "-", string prefix = null, string suffix = null)
        {
            return ((double?)value).ToShowString(digits, substitutes, prefix, suffix);
        }

        public static string ToPercentString(this decimal? value, double dataTotal = 1.0, int digits = 0, string suffix = "%")
        {
            return ((double?)value).ToPercentString(dataTotal, digits, suffix);
        }

        public static string ToShowString(this int? value, string substitutes = "-", string prefix = null, string suffix = null)
        {
            string output = null;
            if (value.HasValue && !double.IsNaN(value.Value))
            {
                output = value.Value.ToString();

                if (!string.IsNullOrEmpty(prefix))
                {
                    output = prefix + output;
                }
                if (!string.IsNullOrEmpty(suffix))
                {
                    output = output + suffix;
                }
            }
            else
            {
                output = substitutes;
            }
            return output;
        }

        /// <summary>
        /// 角度计算平均值
        /// </summary>
        /// <param name="vecA"></param>
        /// <returns></returns>
        public static double AvgAngle(List<double> vecA)
        {
            double tmp_mean = 0;
            double t_h3_mean = 0;
            for (int i = 0; i < vecA.Count; i++)
            {
                if (Math.Abs(tmp_mean - vecA[i]) > 90)
                {
                    if (tmp_mean > vecA[i])
                    {
                        t_h3_mean += vecA[i] + 180;
                    }
                    else
                    {
                        t_h3_mean += vecA[i] - 180;
                    }
                }
                else
                {
                    t_h3_mean += vecA[i];
                }

                tmp_mean = t_h3_mean / (i + 1);
            }

            double avgA = t_h3_mean / vecA.Count;

            if (avgA < 0)
            {
                avgA += 180;
            }
            return avgA;
        }

        public static double AverageEx(this IEnumerable<int> source, double outValue = 0.0)
        {
            double output = outValue;

            if (source != null && source.Count() >= 2)
            {
                output = source.Average();
            }
            return output;
        }
        public static double MaxEx(this IEnumerable<int> source, double outValue = 0.0)
        {
            double output = outValue;

            if (source != null && source.Count() >= 2)
            {
                output = source.Max();
            }
            return output;
        }
        public static double MinEx(this IEnumerable<int> source, double outValue = 0.0)
        {
            double output = outValue;

            if (source != null && source.Count() >= 2)
            {
                output = source.Min();
            }
            return output;
        }


        /// <summary>
        /// 计算不正常的数据的序号，如果SD<阈值，则不计算
        /// </summary>
        public static (bool? warn0, bool? warn1, bool? warn2, bool? warn3, bool? warn4) AScanResultWarnCheck(double? data0, double? data1, double? data2, double? data3, double? data4, double? dataSD, double threshold = 0.05, int digits = 2)
        {
            bool? Warn0 = null;
            bool? Warn1 = null;
            bool? Warn2 = null;
            bool? Warn3 = null;
            bool? Warn4 = null;

            var list = new List<double?>() { data0, data1, data2, data3, data4 }.ToArray();
            if (list.Count(p => p.HasValue) <= 2) return (Warn0, Warn1, Warn2, Warn3, Warn4);

            bool RoundFirstThenCalculate = true;
            if (RoundFirstThenCalculate)
            {
                if (data0.HasValue) data0 = Math.Round(data0.Value, digits, MidpointRounding.AwayFromZero).ToDouble();
                if (data1.HasValue) data1 = Math.Round(data1.Value, digits, MidpointRounding.AwayFromZero).ToDouble();
                if (data2.HasValue) data2 = Math.Round(data2.Value, digits, MidpointRounding.AwayFromZero).ToDouble();
                if (data3.HasValue) data3 = Math.Round(data3.Value, digits, MidpointRounding.AwayFromZero).ToDouble();
                if (data4.HasValue) data4 = Math.Round(data4.Value, digits, MidpointRounding.AwayFromZero).ToDouble();
                list = new List<double?>() { data0, data1, data2, data3, data4 }.ToArray();

                dataSD = list.Where(p => p.HasValue).Select(p => Math.Round(p.Value, digits, MidpointRounding.AwayFromZero)).StandardDeviation();
                if (dataSD.HasValue) dataSD = (double)Math.Round(dataSD.Value, digits, MidpointRounding.AwayFromZero);
            }

            //标准差 >= 阈值，并且至少有3个数值才可以比较
            if (dataSD.HasValue && dataSD.Value >= threshold)
            {
                for (int i = 0; i < list.Length; i++)
                {
                    int? indexWarn = null;
                    if (list[i].HasValue)
                    {
                        var tempList = list.ToList();
                        tempList.RemoveAt(i);
                        //计算除去第i个数据，其余数据的标准差
                        var sd = tempList.StandardDeviation();
                        //如果这个第i个数据对整体标准差影响比较大（第i个数据加剧了数据的离散程度），则第i个数据就是警告
                        if (dataSD.HasValue && !sd.IsNaN())
                        {
                            //if (RoundFirstThenCalculate)
                            //{
                            //    sd = (double)Math.Round(sd, digits, MidpointRounding.AwayFromZero);
                            //}
                            Console.WriteLine($"SD: {dataSD}-{sd}={dataSD - sd}");
                            if (sd < dataSD.Value)
                            {
                                indexWarn = i;
                            }
                        }
                    }

                    if (indexWarn.HasValue)
                    {
                        if (indexWarn.Value == 0) Warn0 = true;
                        if (indexWarn.Value == 1) Warn1 = true;
                        if (indexWarn.Value == 2) Warn2 = true;
                        if (indexWarn.Value == 3) Warn3 = true;
                        if (indexWarn.Value == 4) Warn4 = true;
                    }
                }

            }

            return (Warn0, Warn1, Warn2, Warn3, Warn4);


        }

        /// <summary>
        /// 计算不正常的数据的序号，如果SD<阈值，则不计算
        /// </summary>
        public static (bool? warn0, bool? warn1, bool? warn2, bool? warn3, bool? warn4, double? dataSD) RefResultWarnCheck(double? data0, double? data1, double? data2, double? data3, double? data4, double threshold = 0.10, int digits = 2)
        {
            bool? Warn0 = null;
            bool? Warn1 = null;
            bool? Warn2 = null;
            bool? Warn3 = null;
            bool? Warn4 = null;
            double? dataSD = null;

            var list = new List<double?>() { data0, data1, data2, data3, data4 }.ToArray();
            if (list.Count(p => p.HasValue) <= 2) return (Warn0, Warn1, Warn2, Warn3, Warn4, dataSD);

            bool RoundFirstThenCalculate = true;
            if (RoundFirstThenCalculate)
            {
                if (data0.HasValue) data0 = Math.Round(data0.Value, digits, MidpointRounding.AwayFromZero).ToDouble();
                if (data1.HasValue) data1 = Math.Round(data1.Value, digits, MidpointRounding.AwayFromZero).ToDouble();
                if (data2.HasValue) data2 = Math.Round(data2.Value, digits, MidpointRounding.AwayFromZero).ToDouble();
                if (data3.HasValue) data3 = Math.Round(data3.Value, digits, MidpointRounding.AwayFromZero).ToDouble();
                if (data4.HasValue) data4 = Math.Round(data4.Value, digits, MidpointRounding.AwayFromZero).ToDouble();
                list = new List<double?>() { data0, data1, data2, data3, data4 }.ToArray();

                dataSD = list.Where(p => p.HasValue).Select(p => Math.Round(p.Value, digits, MidpointRounding.AwayFromZero)).StandardDeviation();
                if (dataSD.HasValue) dataSD = (double)Math.Round(dataSD.Value, digits, MidpointRounding.AwayFromZero);
            }

            //标准差 >= 阈值，并且至少有3个数值才可以比较
            if (dataSD.HasValue && dataSD.Value >= threshold)
            {
                for (int i = 0; i < list.Length; i++)
                {
                    int? indexWarn = null;
                    if (list[i].HasValue)
                    {
                        var tempList = list.ToList();
                        tempList.RemoveAt(i);
                        //计算除去第i个数据，其余数据的标准差
                        var sd = tempList.StandardDeviation();
                        //如果这个第i个数据对整体标准差影响比较大（第i个数据加剧了数据的离散程度），则第i个数据就是警告
                        if (dataSD.HasValue && !sd.IsNaN())
                        {
                            //if (RoundFirstThenCalculate)
                            //{
                            //    sd = (double)Math.Round(sd, digits, MidpointRounding.AwayFromZero);
                            //}
                            Console.WriteLine($"SD: {dataSD}-{sd}={dataSD - sd}");
                            if (sd < dataSD.Value)
                            {
                                indexWarn = i;
                            }
                        }
                    }

                    if (indexWarn.HasValue)
                    {
                        if (indexWarn.Value == 0) Warn0 = true;
                        if (indexWarn.Value == 1) Warn1 = true;
                        if (indexWarn.Value == 2) Warn2 = true;
                        if (indexWarn.Value == 3) Warn3 = true;
                        if (indexWarn.Value == 4) Warn4 = true;
                    }
                }

            }

            return (Warn0, Warn1, Warn2, Warn3, Warn4, dataSD);


        }

        //传入一个数组,求出一个数组的最大值的位置
        public static int MaxIndex<T>(T[] arr) where T : IComparable<T>
        {
            var i_Pos = 0;
            var value = arr[0];
            for (var i = 1; i < arr.Length; ++i)
            {
                var _value = arr[i];
                if (_value.CompareTo(value) > 0)
                {
                    value = _value;
                    i_Pos = i;
                }
            }
            return i_Pos;
        }

    }


}